//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <ddk.h>

class EventServer001 : public CDeviceDriver
{
public:
    EventServer001()
    {
        pEvent = NULL;
    }

    CARAPI Read(
        /* [in] */ UInt64 u64Offset,
        /* [in] */ UInt32 bytesToRead,
        /* [out] */ EzByteBuf buffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Write(
        /* [in] */ UInt64 u64Offset,
        /* [in] */ EzByteBuf buffer,
        /* [out] */ UInt32 * pBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ EzByteBuf inBuffer,
        /* [out] */ EzByteBuf * pOutBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    virtual void Dispose() {}

private:
    DzEvent       *pEvent;
};

EXTERN IDeviceDriver * CDECL CreateEventServer0(uint_t uDeviceNo,
    void *pvParameter)
{
    EventServer001 *pDriver = new EventServer001;
    if (NULL == pDriver) {
        kprintf("ERROR:There have no enough memory, %s, %d\n",
                        __FILE__, __LINE__);
        return NULL;
    }

    pDriver->AddRef();

    return pDriver;
}

ECode EventServer001::Read(
    /* [in] */ UInt64 u64Offset,
    /* [in] */ UInt32 bytesToRead,
    /* [out] */ EzByteBuf buffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode EventServer001::Write(
    /* [in] */ UInt64 u64Offset,
    /* [in] */ EzByteBuf buffer,
    /* [out] */ UInt32 * pBytesWritten,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode EventServer001::Control(
    /* [in] */ Handle32 nControlCode,
    /* [in] */ EzByteBuf inBuffer,
    /* [out] */ EzByteBuf * pOutBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    if (NULL != ppCompletionEvent) {
        return E_INVALID_ARGUMENT;
    }

    ECode ec = NOERROR;

    switch (nControlCode) {
        case 0:{
            if (!inBuffer.IsNull() || !pOutBuffer->IsNull()) {
                return E_INVALID_ARGUMENT;
            }

            if (pEvent) {
                delete pEvent;
            }
            else {
                kprintf("There is no event object.\n");
                ec = E_FAIL;
            }

            pEvent = NULL;

            break;
        }

        // manual, unsignal
        case 1:{
            if (!inBuffer.IsNull() || !pOutBuffer->IsNull()) {
                return E_INVALID_ARGUMENT;
            }

            if (pEvent) {
                kprintf("There is has a event object.\n");
                ec = S_FALSE;
            }
            else {
                pEvent = new DzEvent(TRUE, UNSIGNALED);
                if (NULL == pEvent) {
                    ec = E_OUT_OF_MEMORY;
                }
                else {
                    ec = NOERROR;
                }
            }

            break;
        }

        // manual, signal
        case 2:{
            if (!inBuffer.IsNull() || !pOutBuffer->IsNull()) {
                return E_INVALID_ARGUMENT;
            }

            if (pEvent) {
                kprintf("There is has a event object.\n");
                ec = S_FALSE;
            }
            else {
                pEvent = new DzEvent(TRUE, SIGNALED);
                if (NULL == pEvent) {
                    ec = E_OUT_OF_MEMORY;
                }
                else {
                    ec = NOERROR;
                }
            }

            break;
        }

        // auto, unsignaled
        case 3:{
            if (!inBuffer.IsNull() || !pOutBuffer->IsNull()) {
                return E_INVALID_ARGUMENT;
            }

            if (pEvent) {
                kprintf("There is has a event object.\n");
                ec = S_FALSE;
            }
            else {
                pEvent = new DzEvent(FALSE, UNSIGNALED);
                if (NULL == pEvent) {
                    ec = E_OUT_OF_MEMORY;
                }
                else {
                    ec = NOERROR;
                }
            }

            break;
        }

        // auto, signaled
        case 4:{
            if (!inBuffer.IsNull() || !pOutBuffer->IsNull()) {
                return E_INVALID_ARGUMENT;
            }

            if (pEvent) {
                kprintf("There is has a event object.\n");
                ec = S_FALSE;
            }
            else {
                pEvent = new DzEvent(FALSE, SIGNALED);
                if (NULL == pEvent) {
                    ec = E_OUT_OF_MEMORY;
                }
                else {
                    ec = NOERROR;
                }
            }

            break;
        }

        case 5:{
            if (!inBuffer.IsNull()) {
                return E_INVALID_ARGUMENT;
            }

            uint_t uState;

            ec = pEvent->Wait(&uState);

            if ((uint_t)pOutBuffer->GetCapacity() >= sizeof(uint_t)) {
                *(uint_t *)(char *)ebbOutData = uState;
                pOutBuffer->SetUsed(sizeof(uint_t));
            }

            break;
        }

        case 6:{
            if (inBuffer.IsNull()) {
                return E_INVALID_ARGUMENT;
            }

            SystemTime  currentTime;
            SystemTime  leastTime;
            uint_t uWaitTime = *(uint_t *)(char *)inBuffer;
            uint_t uState;

            DzGetSystemTime(&currentTime);
            leastTime.seconds = currentTime.seconds + uWaitTime / 1000;
            if (leastTime.seconds < currentTime.seconds) {
                leastTime.seconds = currentTime.seconds;
            }

            leastTime.microseconds = currentTime.microseconds +
                (uWaitTime % 1000) * 1000;
            if (leastTime.microseconds < currentTime.microseconds) {
                leastTime.microseconds = currentTime.microseconds;
            }

            ec = pEvent->TryWait(uWaitTime, &uState);

            if (S_TIMED_OUT == ec) {
                DzGetSystemTime(&currentTime);
                if (leastTime.seconds > currentTime.seconds) {
                    ec = E_UNEXPECTED;
                }
                else if (leastTime.seconds == currentTime.seconds) {
                    if (leastTime.microseconds > currentTime.microseconds) {
                        ec = E_UNEXPECTED;
                    }
                }
            }

            if ((uint_t)pOutBuffer->GetCapacity() >= sizeof(uint_t)) {
                *(uint_t *)(char *)ebbOutData = uState;
                pOutBuffer->SetUsed(sizeof(uint_t));
            }

            break;
        }

        case 7:{
            if (!pOutBuffer->IsNull()) {
                return E_INVALID_ARGUMENT;
            }

            if (inBuffer.IsNull()) {
                pEvent->Notify();
            }
            else {
                uint_t uState = *(uint_t *)(char *)inBuffer;

                if (0 == uState) {
                    ec = E_INVALID_ARGUMENT;
                }
                else {
                    pEvent->Notify(uState);
                }
            }

            break;
        }

        case 8:{
            if (!pOutBuffer->IsNull() || !inBuffer.IsNull()) {
                return E_INVALID_ARGUMENT;
            }

            pEvent->Clear();

            break;
        }

        default:{
            return E_NOT_IMPLEMENTED;
        }
    }

    return ec;
}
